package tcp

import (
	"errors"
	"fmt"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

// Error type

var (
	ErrConnClosing   = errors.New("use of closed network connection")
	ErrWriteBlocking = errors.New("write buff was blocking")
	ErrReadBlocking  = errors.New("read buff was blocking")
)

type holder interface {
	getCallback() Callback
	getExitChan() chan interface{}
	getSendChanMax() int
	getRecvChanMax() int
	getWaitGroup() *sync.WaitGroup
}

type Conn struct {
	holder    holder
	socket    *net.TCPConn
	closeSync sync.Once
	closeFlag int32
	closeChan chan interface{}
	sendChan  chan []byte
	recvChan  chan []byte
}

func NewConn(socket *net.TCPConn, h holder) *Conn {
	return &Conn{
		holder:    h,
		socket:    socket,
		closeChan: make(chan interface{}),
		sendChan:  make(chan []byte, h.getSendChanMax()),
		recvChan:  make(chan []byte, h.getRecvChanMax()),
	}
}
func (c *Conn) Close() {
	c.closeSync.Do(func() {
		atomic.StoreInt32(&c.closeFlag, 1)
		close(c.closeChan)
		close(c.sendChan)
		close(c.recvChan)
		c.socket.Close()
		c.holder.getCallback().OnClose(c)
	})
}

func (c *Conn) IsClosed() bool {
	return atomic.LoadInt32(&c.closeFlag) == 1
}

func (c *Conn) GetSocket() *net.TCPConn {
	return c.socket
}

func (c *Conn) Send(buff []byte, timeout time.Duration) error {
	if c.IsClosed() {
		return ErrConnClosing
	}
	defer func() {
		if e := recover(); e != nil {
			err := fmt.Errorf("catch panic! error:%v", e)
			c.holder.getCallback().OnError(c, err)
		}
	}()

	if timeout == 0 {
		select {
		case c.sendChan <- buff:
			return nil
		default:
			return ErrWriteBlocking
		}
	} else {
		select {
		case c.sendChan <- buff:
			return nil
		case <-c.closeChan:
			return ErrConnClosing
		case <-time.After(timeout):
			return ErrWriteBlocking
		}
	}

	return nil
}

func (c *Conn) goloop(do func()) {
	c.holder.getWaitGroup().Add(1)
	go func() {
		do()
		c.holder.getWaitGroup().Done()
	}()
}

func (c *Conn) recv() {
	defer func() {
		if e := recover(); e != nil {
			err := fmt.Errorf("catch panic! error:%v", e)
			c.holder.getCallback().OnError(c, err)
		}
		//fmt.Println("exit recv")
		c.Close()
	}()

	buff := make([]byte, 1024)
	for {
		select {
		case <-c.holder.getExitChan():
			return
		case <-c.closeChan:
			return
		default:
		}

		n, err := c.socket.Read(buff)
		if err != nil {
			c.holder.getCallback().OnError(c, err)
			return
		}

		select {
		case c.recvChan <- buff[0:n]:
			break
		default:
			c.holder.getCallback().OnError(c, ErrReadBlocking)
			return
		}
	}
}

func (c *Conn) send() {
	defer func() {
		if e := recover(); e != nil {
			err := fmt.Errorf("catch panic! error:%v", e)
			c.holder.getCallback().OnError(c, err)
		}
		//fmt.Println("exit send")
		c.Close()
	}()

	for {
		select {
		case <-c.holder.getExitChan():
			return
		case <-c.closeChan:
			return
		case buff := <-c.sendChan:
			_, err := c.socket.Write(buff)
			if err != nil {
				c.holder.getCallback().OnError(c, err)
				return
			}
			break
		}
	}
}

func (c *Conn) run() {
	c.holder.getWaitGroup().Add(1)
	defer func() {
		if e := recover(); e != nil {
			err := fmt.Errorf("catch panic! error:%v", e)
			c.holder.getCallback().OnError(c, err)
		}
		//fmt.Println("exit run")
		c.Close()
		c.holder.getWaitGroup().Done()
	}()

	err := c.holder.getCallback().OnConnect(c)
	if err != nil {
		c.holder.getCallback().OnError(c, err)
		return
	}

	c.goloop(c.recv)
	c.goloop(c.send)

	for {
		select {
		case <-c.holder.getExitChan():
			return
		case <-c.closeChan:
			return
		case buff := <-c.recvChan:
			err := c.holder.getCallback().OnMessage(c, buff)
			if err != nil {
				c.holder.getCallback().OnError(c, err)
				return
			}
			break
		}
	}
}
